"""主控制器模块 - PyQt5版本"""
import sys
import os
import threading
from typing import Optional

ROOT_DIR = os.path.join(os.path.dirname(__file__), '..', '..', '..')
sys.path.insert(0, ROOT_DIR)

from PyQt5.QtCore import QObject
from PyQt5.QtWidgets import QMessageBox

from app.gui.models.connection_model import ConnectionModel
from app.gui.models.download_model import DownloadModel, DownloadStatus
from app.gui.views.connection_view import ConnectionView
from app.gui.views.download_view import DownloadView
from app.controller.request_api import api as request_api


class MainController(QObject):
    """主控制器类"""
    
    def __init__(self, parent=None):
        super().__init__(parent)
        self._connection_model = ConnectionModel(self)
        self._download_model = DownloadModel(self)
        self._connection_view = None
        self._download_view = None
        self._download_thread = None
        self._download_cancelled = False
        self._setup_model_callbacks()
        request_api.set_progress_callback(self._on_api_progress)
        self._init_data()
    
    def set_views(self, connection_view, download_view):
        """设置视图引用并绑定事件"""
        self._connection_view = connection_view
        self._download_view = download_view
        
        self._connection_view.connect_requested.connect(self._on_connect)
        self._connection_view.disconnect_requested.connect(self._on_disconnect)
        self._connection_view.refresh_ports_requested.connect(self._on_refresh_ports)
        
        self._download_view.file_selected.connect(self._on_select_file)
        self._download_view.download_requested.connect(self._on_start_download)
        self._download_view.download_stopped.connect(self._on_stop_download)
        
        self._refresh_view_data()
    
    def _setup_model_callbacks(self):
        """设置模型回调"""
        self._connection_model.status_changed.connect(self._on_connection_status_changed)
        self._download_model.progress_changed.connect(self._on_download_progress_changed)
        self._download_model.status_changed.connect(self._on_download_status_changed)
        self._download_model.log_added.connect(self._on_download_log_added)
    
    def _init_data(self):
        """初始化数据"""
        self._refresh_com_ports()
    
    def _refresh_view_data(self):
        """刷新视图数据"""
        if not self._connection_view or not self._download_view:
            return
        
        config = self._connection_model.get_config()
        self._connection_view.set_connection_config({
            'com_port': config.com_port,
            'tx_can_id': config.tx_can_id,
            'rx_can_id': config.rx_can_id,
            'baudrate': config.baudrate
        })
        
        ports = self._connection_model.get_available_ports()
        self._connection_view.set_com_ports(ports)
        
        # 设置常用的10进制CAN ID选项（1-64）
        decimal_can_ids = [str(i) for i in range(1, 65)]  # 1到64
        self._connection_view.set_can_id_options(decimal_can_ids)
        
        self._connection_view.set_connection_status(
            config.is_connected,
            "已连接" if config.is_connected else "未连接"
        )
    
    def _refresh_com_ports(self):
        """刷新COM口列表"""
        suc, ports, msg = request_api.get_available_com_ports()
        if suc:
            self._connection_model.set_available_ports(ports)
            if self._connection_view:
                self._connection_view.set_com_ports(ports)
            self._download_model.add_log(f"检测到{len(ports)}个COM口")
        else:
            self._download_model.add_log(f"获取COM口失败: {msg}")
    
    def _validate_can_id(self, can_id_str, name):
        """验证CAN ID格式（10进制）"""
        try:
            can_id = int(can_id_str)
            if 1 <= can_id <= 64:  # 1-64范围
                return True, can_id
            else:
                if self._connection_view:
                    QMessageBox.warning(self._connection_view, "配置错误", f"{name}超出范围(1-64): {can_id}")
                return False, 0
        except ValueError:
            if self._connection_view:
                QMessageBox.warning(self._connection_view, "配置错误", f"{name}格式无效，请输入数字: {can_id_str}")
            return False, 0
    
    def _on_connect(self, config):
        """连接事件处理"""
        suc, tx_id = self._validate_can_id(config['tx_can_id'], "发送CAN ID")
        if not suc:
            return
        
        # 接收CAN ID固定为0
        rx_id = 0
        
        if self._connection_view:
            self._connection_view.set_connecting_status("正在连接...")
        
        self._download_model.add_log(f"开始连接 - COM口: {config['com_port']}, 发送ID: {config['tx_can_id']}, 接收ID: 0")
        
        def connect_thread():
            try:
                suc, msg = request_api.init_connection(
                    config['com_port'], tx_id, rx_id, config['baudrate']
                )
                
                if not suc:
                    self._download_model.add_log(f"连接初始化失败: {msg}")
                    self._connection_model.set_connection_status(False)
                    return
                
                suc, msg = request_api.connect_target(retry_count=3)
                if suc:
                    self._download_model.add_log(f"连接成功: {msg}")
                    
                    # 获取实际使用的COM口（可能与用户选择的不同）
                    actual_suc, actual_port, actual_msg = request_api.get_actual_com_port()
                    if actual_suc and actual_port != config['com_port']:
                        self._download_model.add_log(f"实际使用COM口: {actual_port} (原选择: {config['com_port']})")
                        # 更新模型中的COM口为实际使用的COM口
                        self._connection_model.set_com_port(actual_port)
                        # 同时更新前端显示的COM口
                        if self._connection_view:
                            # 刷新COM口列表并设置为实际使用的COM口
                            self._refresh_com_ports()
                            # 更新连接配置显示
                            self._connection_view.set_connection_config({
                                'com_port': actual_port,
                                'tx_can_id': config['tx_can_id'],
                                'rx_can_id': "0",
                                'baudrate': config['baudrate']
                            })
                    else:
                        # 使用原选择的COM口
                        self._connection_model.set_com_port(config['com_port'])
                    
                    self._connection_model.set_tx_can_id(config['tx_can_id'])
                    self._connection_model.set_rx_can_id("0")  # 固定为0
                    self._connection_model.set_baudrate(config['baudrate'])
                    self._connection_model.set_connection_status(True)
                else:
                    self._download_model.add_log(f"连接失败: {msg}")
                    self._connection_model.set_connection_status(False)
            
            except Exception as e:
                self._download_model.add_log(f"连接异常: {str(e)}")
                self._connection_model.set_connection_status(False)
        
        threading.Thread(target=connect_thread, daemon=True).start()
    
    def _on_disconnect(self):
        """断开连接事件处理"""
        self._download_model.add_log("正在断开连接...")
        try:
            suc, msg = request_api.disconnect()
            if suc:
                self._download_model.add_log(f"断开连接成功: {msg}")
            else:
                self._download_model.add_log(f"断开连接失败: {msg}")
        except Exception as e:
            self._download_model.add_log(f"断开连接异常: {str(e)}")
        
        self._connection_model.set_connection_status(False)
    
    def _on_refresh_ports(self):
        """刷新COM口事件处理"""
        self._refresh_com_ports()
    
    def _on_select_file(self, file_path):
        """文件选择事件处理"""
        self._download_model.add_log(f"选择固件文件: {os.path.basename(file_path)}")
        
        try:
            file_size = os.path.getsize(file_path)
            file_name = os.path.basename(file_path)
            
            self._download_model.set_firmware_info(
                file_path=file_path,
                file_name=file_name,
                file_size=file_size
            )
            
            if self._download_view:
                self._download_view.set_firmware_info({
                    'file_name': file_name,
                    'file_size': file_size
                })
            
        except Exception as e:
            self._download_model.add_log(f"读取文件信息失败: {str(e)}")
    
    def _on_start_download(self, file_path):
        """开始下载事件处理"""
        if not self._connection_model.get_config().is_connected:
            QMessageBox.warning(self._download_view, "错误", "请先连接设备")
            return
        
        if self._download_thread and self._download_thread.is_alive():
            QMessageBox.warning(self._download_view, "警告", "下载正在进行中")
            return
        
        self._download_model.reset()
        self._download_model.set_status(DownloadStatus.LOADING_FIRMWARE, "准备下载...")
        self._download_cancelled = False
        
        self._download_thread = threading.Thread(
            target=self._download_firmware_thread, 
            args=(file_path,),
            daemon=True
        )
        self._download_thread.start()
    
    def _on_stop_download(self):
        """停止下载事件处理"""
        self._download_cancelled = True
        self._download_model.add_log("用户取消下载")
        self._download_model.set_status(DownloadStatus.CANCELLED, "下载已取消")
    
    def _download_firmware_thread(self, file_path):
        """固件下载线程"""
        try:
            self._download_model.add_log(f"加载固件文件: {os.path.basename(file_path)}")
            suc, msg = request_api.load_firmware(file_path)
            
            if not suc:
                self._download_model.add_log(f"加载固件失败: {msg}")
                self._download_model.set_status(DownloadStatus.FAILED, "加载固件失败")
                return
            
            if self._download_cancelled:
                return
            
            self._download_model.add_log(f"固件加载成功: {msg}")
            self._download_model.set_status(DownloadStatus.DOWNLOADING, "开始下载固件...")
            
            suc, msg = request_api.download_firmware()
            
            if self._download_cancelled:
                return
            
            if suc:
                self._download_model.add_log(f"固件下载成功: {msg}")
                self._download_model.set_status(DownloadStatus.COMPLETED, "下载完成")
                self._download_model.update_progress(100.0, "下载完成")
            else:
                self._download_model.add_log(f"固件下载失败: {msg}")
                self._download_model.set_status(DownloadStatus.FAILED, "下载失败")
                
        except Exception as e:
            self._download_model.add_log(f"下载异常: {str(e)}")
            self._download_model.set_status(DownloadStatus.FAILED, "下载异常")
    
    def _on_api_progress(self, progress, message):
        """API进度回调"""
        self._download_model.update_progress(progress, message)
    
    def _on_connection_status_changed(self, connected):
        """连接状态变化回调"""
        if self._connection_view:
            status_msg = "已连接" if connected else "未连接"
            self._connection_view.set_connection_status(connected, status_msg)
    
    def _on_download_progress_changed(self, progress):
        """下载进度变化回调"""
        if self._download_view:
            self._download_view.set_progress(progress.percentage, progress.message)
    
    def _on_download_status_changed(self, status):
        """下载状态变化回调"""
        if self._download_view:
            status_map = {
                DownloadStatus.IDLE: "idle",
                DownloadStatus.LOADING_FIRMWARE: "downloading",
                DownloadStatus.CONNECTING: "downloading", 
                DownloadStatus.DOWNLOADING: "downloading",
                DownloadStatus.COMPLETED: "completed",
                DownloadStatus.FAILED: "failed",
                DownloadStatus.CANCELLED: "idle"
            }
            view_status = status_map.get(status, "idle")
            self._download_view.set_download_status(view_status)
    
    def _on_download_log_added(self, message):
        """下载日志添加回调"""
        if self._download_view:
            import datetime
            timestamp = datetime.datetime.now().strftime("%H:%M:%S")
            self._download_view.add_log(f"[{timestamp}] {message}")
    
    def cleanup(self):
        """清理资源"""
        if self._download_thread and self._download_thread.is_alive():
            self._download_cancelled = True
            self._download_thread.join(timeout=2)
        
        try:
            request_api.disconnect()
        except:
            pass